#include"slide.h"
#include "../src_post/PostProcessing.h"

using namespace std;

//1.推线
int SlideLine::pushSingleLine(vector<Line>& polyLines, Line& slideLine, const Point offset, const bool postOn) {
	int index = -1;
	index = getPosition(polyLines, slideLine);
	if (index < 0) return -1;
	Line backupLine = slideLine;
	bool resultJudge = true;			//默认成功
	pushSingleSeg(slideLine, offset);	//推挤线段
	determineConnection(polyLines, index, slideLine);	//处理各线间位置关系
	resultJudge = judgement(backupLine, slideLine);		//判断当前推挤的线是否到达临界值  [1]true:推挤完未到达边界 [2]false:推挤完小于边界
	if (resultJudge) {
		inspectSingleLines(slideLine, polyLines);		//推挤完后处理
	}
	else {
		critical_value = true;
		if (polyLines.size() == 1) {
			return 0;
		}
		int NewIndex = getPosition(polyLines, slideLine); //新位置，并将slideLine设置为polyLines中对应线段的引用
		index = NewIndex;
		Point tempFrontVec, tempBackVec;		//向前向后到临界位置的方向向量
		double tempFrontDist, tempBackDist;		//向前向后到临界位置的偏移量
		Point tempCrossingPoint;				//相邻部分的交点
		if (index == 0 && backupLine.Pt1.locked == false) {							//推起始线且起始端点不固定
			Point slideVerticalVec = backupLine.Pt1 - slideLine.Pt1;
			Line backLine = polyLines[index + 1];
			Point backVec = backLine.getVector();
			//getValue
			tempCrossingPoint = getInsection(slideVerticalVec, backupLine.Pt1, backVec, backLine.Pt2);
			tempFrontDist = MIN_NUM;
			tempBackDist = sqrt(MIN_NUM * MIN_NUM * 2);
			tempFrontVec = backupLine.Pt1 - slideLine.Pt1;
			tempBackVec = backVec;
		}
		else if (index == polyLines.size() - 1 && backupLine.Pt2.locked == false) {  //推终止线且终止端点不固定
			Point slideVerticalVec = backupLine.Pt2 - slideLine.Pt2;
			Line frontLine = polyLines[index - 1];
			Point frontLineVec = frontLine.getVector();
			//getValue
			tempCrossingPoint = getInsection(slideVerticalVec, backupLine.Pt2, frontLineVec, frontLine.Pt1);
			tempFrontDist = sqrt(MIN_NUM * MIN_NUM * 2);
			tempBackDist = MIN_NUM;
			tempFrontVec = frontLineVec * (-1);
			tempBackVec = backupLine.Pt2 - slideLine.Pt2;
		}
		else {
			Line frontLine = polyLines[NewIndex - 1];
			Point frontLineVec = frontLine.getVector();
			Line backLine = polyLines[NewIndex + 1];
			Point backLineVec = backLine.getVector();
			//getValue
			tempCrossingPoint = getInsection(frontLineVec, frontLine.Pt1, backLineVec, backLine.Pt2);
			tempBackDist = tempFrontDist = sqrt(MIN_NUM * MIN_NUM / 2);
			tempFrontVec = frontLineVec * (-1);
			tempBackVec = backLineVec;
			index = NewIndex;  //位置发生变化
		}
		tempFrontVec = tempFrontVec / norm(tempFrontVec);
		tempBackVec = tempBackVec / norm(tempBackVec);
		Point frontStartPoint = tempCrossingPoint + tempFrontVec * tempFrontDist;
		Point backEndPoint = tempCrossingPoint + tempBackVec * tempBackDist;
		//give Value
		if (index == 0 && slideLine.Pt1.locked == false) {
			slideLine.Pt1 = polyLines[index].Pt1 = frontStartPoint;
			slideLine.Pt2 = polyLines[index].Pt2 = polyLines[index + 1].Pt1 = backEndPoint;
		}
		else if (index == polyLines.size() - 1 && slideLine.Pt2.locked == false) {
			slideLine.Pt1 = polyLines[index - 1].Pt2 = polyLines[index].Pt1 = frontStartPoint;
			slideLine.Pt2 = polyLines[index].Pt2 = backEndPoint;
		}
		else {
			slideLine.Pt1 = polyLines[index - 1].Pt2 = polyLines[index].Pt1 = frontStartPoint;
			slideLine.Pt2 = polyLines[index].Pt2 = polyLines[index + 1].Pt1 = backEndPoint;
		}
	}

	mergeLines(polyLines);
	//执行后处理
	if (postOn) slidePostProcessing(polyLines);

	if (m_inGreed) {		//取网格点
		for (auto& line : polyLines) {
			line.Pt1.x = round(line.Pt1.x), line.Pt1.y = round(line.Pt1.y);
			line.Pt2.x = round(line.Pt2.x), line.Pt2.y = round(line.Pt2.y);
		}
	}

	return 0;
}
// 确定推挤线和其他线的连接关系
void SlideLine::determineConnection(vector<Line>& lineVec, int index, Line& slideline) {
	int olddir, newdir; //参考点基于slideLine的方向
	bool signal = false;
	Point preCrossingPoint, backCrossingPoint;  //交点
	Point prePoint, backPoint; //靠前的点
	Point slideVec;  //slide线段的方向向量
	Point tempVec;  //和slide线段相交有交点的线段的方向向量
	Point offsetVec; //推挤方向向量

	Line tempLine;
	Line backUpSlideLine; //备份slideline
	Line newPINLine;  //推挤到端线位置，需要引出新的一条线
	Line preSlideLine = lineVec[index];  //推挤前推挤线段
	vector<Line> removeLineVec;  //需要移除的线段的集合
	vector<Line> backUpLineVec; //备份
	slideVec = slideline.Pt2 - slideline.Pt1;
	backUpLineVec = lineVec;
	backUpSlideLine = slideline;

	offsetVec = slideline.Pt2 - preSlideLine.Pt2;   // 线段的移动方向
	offsetVec = getSlideProjection(offsetVec, rotate(slideVec, 90));  // 真实推挤方向（法线方向的推挤方向

	if (index == (lineVec.size() - 1)) {  //终止线段
		backPoint = preSlideLine.Pt2;
		backCrossingPoint = (backPoint.locked == true) ? getPINCrossingPoint(backPoint, backUpSlideLine, false) : slideline.Pt2;
		slideline.Pt2 = lineVec[index].Pt2 = backCrossingPoint;
		if (backPoint.locked == true) {
			newPINLine.Pt1 = backCrossingPoint;
			newPINLine.Pt2 = backPoint;
			lineVec.emplace_back(newPINLine);
		}
	}
	if (index == 0) {   //起始线段
		prePoint = preSlideLine.Pt1;
		preCrossingPoint = (prePoint.locked == true) ? getPINCrossingPoint(prePoint, backUpSlideLine, true) : slideline.Pt1;
		slideline.Pt1 = lineVec[index].Pt1 = preCrossingPoint;
		if (prePoint.locked == true) {
			newPINLine.Pt1 = prePoint;
			newPINLine.Pt2 = lineVec[index].Pt1 = preCrossingPoint;  //赋值
			lineVec.insert(lineVec.begin(), newPINLine);
			signal = true;
		}
	}


	//往前SlideLine和前面线段的交点： 即SlideLine起点的确定
	for (int i = index - 1; i >= 0; --i) {
		tempLine = backUpLineVec[i];  //依次取出前面的每一条线段（包括推挤线段本身本身）
		tempVec = tempLine.Pt1 - tempLine.Pt2;
		prePoint = tempLine.Pt1;
		olddir = direction(preSlideLine, prePoint);
		newdir = direction(backUpSlideLine, prePoint);

		if (isParallel_lines(slideline, tempLine) && i != 0) {
			removeLineVec.emplace_back(tempLine);  // 没有遍历到起始线的平行线段都是要共线走的  --- 20240110 Update
			continue;
		}

		// 遇到垂直线提前判断  2024/03/26 GeUpdate
		if (CheckLinesPerpendicular(slideline, tempLine) && i != 0 && olddir != newdir) {
			removeLineVec.emplace_back(tempLine);
			continue;
		}

		if (olddir == newdir || !(inspectVectorsDirection(offsetVec, tempVec))) {   // 交点在这条线上
			preCrossingPoint = getInsection(slideVec, slideline.Pt2, tempVec, tempLine.Pt2);
			slideline.Pt1 = lineVec[i].Pt2 = lineVec[index].Pt1 = preCrossingPoint;
			break;
		}
		else if (olddir != newdir && i == 0) { //已经循环到起始线位置
			preCrossingPoint = getPINCrossingPoint(prePoint, backUpSlideLine, true);
			slideline.Pt1 = lineVec[i].Pt2 = lineVec[index].Pt1 = preCrossingPoint;
		}
		else {
			removeLineVec.emplace_back(tempLine);
		}
	}

	//往后SlideLine和后面线段的交点 ： 即SlideLine终点的确定
	for (int j = index + 1; j < backUpLineVec.size(); ++j) {
		tempLine = backUpLineVec[j];  //依次取出前面的每一条线段（包括推挤线段本身本身）
		tempVec = tempLine.Pt2 - tempLine.Pt1;
		backPoint = tempLine.Pt2;
		olddir = direction(preSlideLine, backPoint);
		newdir = direction(backUpSlideLine, backPoint);

		if (isParallel_lines(slideline, tempLine) && j != backUpLineVec.size() - 1) {
			removeLineVec.emplace_back(tempLine);  // 没有遍历到终止线的平行线段都是要共线走的  --- 20240110 Update
			continue;
		}

		if (olddir == newdir || !(inspectVectorsDirection(offsetVec, tempVec))) {   //交点在这条线上
			backCrossingPoint = getInsection(slideVec, slideline.Pt2, tempVec, tempLine.Pt2);
			(signal == true) ? lineVec[j + 1].Pt1 = lineVec[index + 1].Pt2 = slideline.Pt2 = backCrossingPoint
				: lineVec[j].Pt1 = lineVec[index].Pt2 = slideline.Pt2 = backCrossingPoint;
			break;
		}
		else if (olddir != newdir && j == backUpLineVec.size() - 1) { //已经循环到终止线位置
			backCrossingPoint = getPINCrossingPoint(backPoint, backUpSlideLine, false);
			(signal == true) ? lineVec[j + 1].Pt1 = lineVec[index + 1].Pt2 = slideline.Pt2 = backCrossingPoint
				: lineVec[j].Pt1 = lineVec[index].Pt2 = slideline.Pt2 = backCrossingPoint;
		}
		else {
			removeLineVec.emplace_back(tempLine);
		}
	}

	//删除推挤前后包含的线
	for (int k = 0; k < removeLineVec.size(); ++k) {
		removeLine(removeLineVec[k], lineVec);
	}
}
void SlideLine::inspectSingleLines(Line& slideLine, vector<Line>& lineVec) {
	int index;
	//int numSize;
	bool isParallel_Line;
	Line tempLine;
	vector<Line> reLineVec;  //备份

	//初始化
	isParallel_Line = false;
	//numSize = lineVec.size();
	reLineVec = lineVec;

	//删除长度为0的线段
	for (int i = 0; i < reLineVec.size(); ++i) {
		tempLine = reLineVec[i];
		if (tempLine.Pt1 == tempLine.Pt2) { //说明该线段起点和终点重合
			removeLine(tempLine, lineVec);
		}
	}

	if (lineVec.size() == 1) {
		return;
	}

	index = getPosition(lineVec, slideLine);  //index 只要不是 -1 一定是在中间，此时的slideLine是推挤完的
	//cout << "index:" << index << endl;
	if (index == -1) {
		cout << "Error! Not positioned to the pushed line!" << endl;
		return;   // 错误——定位失败
	}
	if (index == 0) {  //起始线
		isParallel_Line = isParallel_lines(slideLine, lineVec[index + 1]); //和后一条线是否平行
		if (isParallel_Line) {
			slideLine.setPt2(lineVec[index + 1].Pt2);
			lineVec[index] = slideLine;
			removeLine(lineVec[index + 1], lineVec);
			//index = index + 1; //slideLine 位置前移
			isParallel_Line = false;
		}
	}
	else if (index == lineVec.size() - 1) {
		isParallel_Line = isParallel_lines(slideLine, lineVec[index - 1]); //和前一条线是否平行
		if (isParallel_Line) {
			slideLine.setPt1(lineVec[index - 1].Pt1);
			lineVec[index] = slideLine;
			removeLine(lineVec[index - 1], lineVec);
			index = index - 1; //slideLine 位置前移
			isParallel_Line = false;
		}
	}
	else {
		isParallel_Line = isParallel_lines(slideLine, lineVec[index - 1]); //和前一条线是否平行
		if (isParallel_Line) {
			slideLine.setPt1(lineVec[index - 1].Pt1);
			lineVec[index] = slideLine;
			removeLine(lineVec[index - 1], lineVec);
			index = index - 1; //slideLine 位置前移
			isParallel_Line = false;
		}
		isParallel_Line = isParallel_lines(slideLine, lineVec[index + 1]); //和后一条线是否平行
		if (isParallel_Line) {
			slideLine.setPt2(lineVec[index + 1].Pt2);
			lineVec[index] = slideLine;
			removeLine(lineVec[index + 1], lineVec);
			index = index + 1; //slideLine 位置前移
			isParallel_Line = false;
		}
	}
}

void SlideLine::pushSingleSeg(Line& line, const Point& offset) {
	double angleNormal = atan2(line.Pt2.y - line.Pt1.y, line.Pt2.x - line.Pt1.x) + PI / 2;   // 线段的法线的角度
	Point vecNormal = Point(cos(angleNormal), sin(angleNormal));  //法线的单位向量
	double pushDistance = vecNormal * offset;   // 两向量点积：移动方向在法线方向的移动距离
	line.Pt1 = line.Pt1 + vecNormal * pushDistance;
	line.Pt2 = line.Pt2 + vecNormal * pushDistance;
}
//求解offset在baseVec方向上的投影方向
Point SlideLine::getSlideProjection(const Point& offsetVec, const Point& baseVec) {
	Point newBaseVec = baseVec;
	Point newOffsetVec = offsetVec;
	newBaseVec = newBaseVec / norm(newBaseVec);
	if (newOffsetVec != Point(0, 0)) {
		NormalizeVec(newOffsetVec);  // 单位化 -- 2024-01-11 Update
	}
	Point projectionVec;
	double isSameDir = newOffsetVec * newBaseVec;   // 判断两个向量方向是否相同（用单位向量）
	if (abs(isSameDir) < CountMinValue) {
		isSameDir = 0;
	}
	if (isSameDir >= 0) {    // Same
		projectionVec = newBaseVec * isSameDir;
	}
	else {
		newBaseVec = newBaseVec * (-1);
		double newDotValue = newOffsetVec * newBaseVec;
		projectionVec = newBaseVec * newDotValue;
	}

	return projectionVec;
}

//计算推挤线段和固定参考点的连线的新的交点  front用来判断往前算交点还是往后  front ==  true： 往前
Point SlideLine::getPINCrossingPoint(Point referencePoint, Line slideline, bool front) {
	int rPDir;
	double rotateAngle = 45;//默认逆时针45
	Point slideVec;
	Point crossingP;

	if (front == false) {  //终止线交换两点
		exchangePoints(slideline.Pt2, slideline.Pt1);
	}
	rPDir = direction(slideline, referencePoint);
	slideVec = slideline.Pt2 - slideline.Pt1;
	slideVec = slideVec / norm(slideVec);
	if (rPDir > 0) {
		rotateAngle = -rotateAngle;
	}

	crossingP = getInsection(slideVec, slideline.Pt2, rotate(slideVec, rotateAngle), referencePoint);

	return crossingP;
}
/*
* @brief 判断当前推挤线段是否到达临界值情况
* @line 存放推挤线
* @return true： can push  false：can't push
*/
bool SlideLine::judgement(Line preLine, const Line& line) const {
	double distance;

	Point vec_line = Point(line.Pt2) - line.Pt1;
	Point vecPreLine = Point(preLine.Pt2) - preLine.Pt1;
	double resultN;
	resultN = vec_line * vecPreLine;

	//cout << "vec_line：" << vec_line << endl;
	distance = sqrt(vec_line * vec_line);
	//cout << "distance：" << distance << endl;
	if (distance >= MIN_NUM && resultN > 0) {
		//cout << "mei you dadaolinjiezhi!" << endl;
		return true;
	}
	else
		return false;
}
//后处理
void SlideLine::slidePostProcessing(vector<Line>& polyLines) {
	if (polyLines.empty()) return;
	vector<Line> fixLines = { polyLines[0], polyLines.back() };
	vector<PPObs> nullObss;
	vector<double> spaces((int)polyLines.size(), 0.01);
	for (auto& line : polyLines) {
		line.width = 0.5;
	}
	//1.创建后处理对象，并设置基本参数
	PostProcessingV2 post2;
	post2.setWireSpacing(spaces);
	post2.setMiter(2);
	//post2.setLockLines(lockLines_);
	//2.如果是差分线，设置差分线参数，如果不是，则不需要这一行
	//post2.setDiffInfo(DiffInfo(diffInfo_dirs, diffGaps_));
	//3.如果有需要，可修改后处理模式，1,2,3,4代表4种模式，会批量修改后处理的可选参数
	//4.如果有需要，调用不同接口可再次调整options，覆盖前面设置的参数（包括基本参数和可选参数）
	post2.setAllowDRCs(true);
	post2.setAllowEnlargeBoundary(true);
	post2.setFixHalfObsLine(false);
	post2.setFixBoundaryOnly(true);
	post2.setFixCuteAngel(true);
	post2.setRemainTimes(1.0);
	//5.调用后处理，算法正常结束返回0，异常结束返回负数，不同负数表示不同类型的异常
	int result = post2.HuglinesPostProcessing(polyLines, fixLines, nullObss);
	//6.调试，检查后处理是否正常返回
	if (result < 0) {
		std::cout << "后处理异常终止并返回，Error code= " << -result << endl;
	}
}



//2.推Pin
int SlideLine::pushOnePt(vector<Line>& polyLines, Point& pt, const Point offset) {
	bool isPt1 = false;
	bool moveNeighbor = false;
	int len = polyLines.size();
	int index = getPtLines(polyLines, pt, isPt1);

	Point& movePt = isPt1 ? polyLines[index].Pt1 : polyLines[index].Pt2;
	Point& movePtNeighbor = isPt1 ? polyLines[index - 1 < 0 ? len - 1 : index - 1].Pt2 : polyLines[index + 1 < len ? index + 1 : 0].Pt1;
	moveNeighbor = movePtNeighbor == movePt;
	movePt = movePt + offset;
	if (m_inGreed) {	//开启网格
		movePt.x = round(movePt.x), movePt.y = round(movePt.y);
	}
	if (!moveNeighbor) {	//不是首尾相连，说明退的是Pin点，推完需清空布线信息
		return 1;
	}
	movePtNeighbor = movePtNeighbor + offset;	//是首尾相连，则需要更新邻居点，并且更新布线信息
	if (m_inGreed) {	//开启网格
		movePtNeighbor.x = round(movePtNeighbor.x), movePtNeighbor.y = round(movePtNeighbor.y);
	}
	return 0;
}

int SlideLine::getPtLines(const std::vector<Line>& lines, const Point& pt, bool& isPt1) {
	//i为序号,isPt1标记是该序号线段的起点还是终点
	if (lines.empty()) return -1;
	for (int i = 0; i < lines.size(); ++i) {
		if (lines[i].Pt1 == pt) {
			isPt1 = true;
			return i;
		}
		else if (lines[i].Pt2 == pt) {
			isPt1 = false;
			return i;
		}
	}
	return -1;
}